package sfeir.ergosum.tools;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;

import sfeir.ergosum.R;
import sfeir.ergosum.model.Person;
import sfeir.ergosum.model.spreadsheet.SpreadsheetEntry;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

import com.google.gdata.client.spreadsheet.FeedURLFactory;
import com.google.gdata.client.spreadsheet.SpreadsheetService;
import com.google.gdata.data.Link;
import com.google.gdata.data.spreadsheet.CellFeed;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import com.google.gdata.data.spreadsheet.WorksheetFeed;

public class SpreadsheetClient extends Thread {

    private static final String NS_GACL = "http://schemas.google.com/acl/2007";
    private static final String NS_ATOM = "http://www.w3.org/2005/Atom";
    public Handler handler;
    public Map<String, String> mTokens = new HashMap<String, String>();
    private ResponseHandler mResponseHandler;
    private HttpClient mHttpClient = new DefaultHttpClient();
    private volatile String pass;
    private volatile String email;
    private static FeedURLFactory feedFactory = FeedURLFactory.getDefault();

    public SpreadsheetClient(ResponseHandler responseHandler) {
        super("SpreadsheetClient");
        this.mResponseHandler = responseHandler;
        HttpConnectionParams.setConnectionTimeout(mHttpClient.getParams(),
                10000);
    }

    @Override
    public void run() {
        Looper.prepare();

        handler = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case R.id.request_login:
                    if (null != mTokens.get(msg.obj))
                        mResponseHandler.obtainMessage(msg.arg1,
                                mTokens.get(msg.obj)).sendToTarget();
                    String token = doClientLogin((String) msg.obj);
                    mTokens.put((String) msg.obj, token);
                    if (null == token)
                        return;
                    Message m = mResponseHandler.obtainMessage(msg.arg1);
                    m.obj = token;
                    m.sendToTarget();
                    break;
                case R.id.request_init:
                    Person p = getPerson((String) msg.obj);
                    Message mp = mResponseHandler
                            .obtainMessage(R.id.client_get_person);
                    mp.obj = p;
                    mp.sendToTarget();
                    break;
                case R.id.request_spreadsheets:
                    List<SpreadsheetEntry> spreadsheetKeys = listSpreadsheets();
                    Message ms = mResponseHandler
                            .obtainMessage(R.id.client_get_spreadsheets);
                    ms.obj = spreadsheetKeys;
                    ms.sendToTarget();
                    break;
                case R.id.request_new_spreadsheet:
                    SpreadsheetEntry createdSpreadsheet = newSpreadsheet(
                            "HiveGotContact contact", (String) msg.obj);
                    Message mn = mResponseHandler
                            .obtainMessage(R.id.client_new_spreadsheet);
                    mn.obj = createdSpreadsheet;
                    mn.sendToTarget();
                    break;
                case R.id.request_update_spreadsheet:
                    SpreadsheetEntry updatedSpreadsheet = updateSpreadsheet((String) msg.obj);
                    mResponseHandler
                            .obtainMessage(R.id.client_updated_spreadsheet,
                                    updatedSpreadsheet).sendToTarget();
                    break;
                case R.id.request_share_world:
                    String key = (String) msg.obj;
                    // shareWithTheWorld(key);
                    mResponseHandler.obtainMessage(R.id.client_shared_world,
                            key).sendToTarget();
                    break;
                case R.id.request_new_backup_spreadsheet:
                    if (msg.obj != null) {
                        SpreadsheetEntry createdBackupSpreadsheet = newSpreadsheet(
                                "HiveGotContact Backup contact - " + new Date().toGMTString(), (String)msg.obj);
                        mResponseHandler.obtainMessage(
                                R.id.client_new_backup_spreadsheet,
                                createdBackupSpreadsheet).sendToTarget();

                    }
                    break;
                case R.id.request_restore_spreadsheet:
                    mResponseHandler.obtainMessage(
                            R.id.client_restore_spreadsheet).sendToTarget();
                    CellFeed listWorksheetEntry = getWorksheetEntry((String) msg.obj);
                    mResponseHandler.obtainMessage(R.id.client_restore_spreadsheet, listWorksheetEntry).sendToTarget();
                default:
                    break;
                }
            }
        };
        mResponseHandler.obtainMessage(R.id.httpclient_ready).sendToTarget();

        Looper.loop();
    }

    protected SpreadsheetEntry updateSpreadsheet(String obj) {
        SpreadsheetEntry updatedSpreadsheet = null;
        InputStream is = null;
        String[] params = obj.split("\n", 2);
        try {
            HttpPut request = new HttpPut(new URI(
                    "http://docs.google.com/feeds/media/private/full/spreadsheet%3A"
                            + params[0]));
            request.setHeader("Authorization", "GoogleLogin auth="
                    + mTokens.get("writely"));
            request.setHeader("GData-Version", "2");
            request.setHeader("Slug", "HiveGotContact Profile");
            request.setHeader("If-Match", "*");
            StringEntity entity = new StringEntity(params[1]);
            entity.setContentType("text/csv");
            request.setEntity(entity);
            request.setHeader("Content-Type", "text/csv");
            HttpResponse response = mHttpClient.execute(request);
            Log.w("SpreadsheetClient", response.getStatusLine().toString());
            is = response.getEntity().getContent();
            updatedSpreadsheet = new SpreadsheetFeedParser(
                    new InputStreamReader(is)).parseNewSpreadsheet();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return updatedSpreadsheet;
    }

    protected SpreadsheetEntry newSpreadsheet(String title, String contents) {
        SpreadsheetEntry newSpreadsheet = null;
        InputStream is = null;
        try {
            HttpPost request = new HttpPost(new URI(
                    "http://docs.google.com/feeds/documents/private/full"));
            request.setHeader("Authorization", "GoogleLogin auth="
                    + mTokens.get("writely"));
            request.setHeader("GData-Version", "2");
            // TODO : ne met pas param et val en headers, essayer avec un batch
            // de row insert.
            String entry = null;
            try {
                StringWriter xmlEntry = new StringWriter();
                XmlSerializer serializer = XmlPullParserFactory.newInstance()
                        .newSerializer();
                serializer.setOutput(xmlEntry);
                serializer.startDocument("utf-8", null);
                serializer.setPrefix("atom", NS_ATOM);
                serializer.startTag(NS_ATOM, "entry").startTag(NS_ATOM,
                        "category").attribute(null, "scheme",
                        "http://schemas.google.com/g/2005#kind").attribute(
                        null, "term",
                        "http://schemas.google.com/docs/2007#spreadsheet")
                        .endTag(NS_ATOM, "category").startTag(NS_ATOM, "title")
                        .text(title).endTag(NS_ATOM, "title").endTag(NS_ATOM,
                                "entry").endDocument();
                entry = xmlEntry.toString();
            } catch (XmlPullParserException e) {
                e.printStackTrace();
            }
            StringEntity entity = new StringEntity("--END_OF_PART\n"
                    + "Content-Type: application/atom+xml\n\n" + entry
                    + "\n\n--END_OF_PART\n" + "Content-Type: text/csv\n\n"
                    + contents + "\n\n--END_OF_PART--");
            entity.setContentType("multipart/related; boundary=END_OF_PART");
            request.setEntity(entity);
            request.setHeader("Content-Type",
                    "multipart/related; boundary=END_OF_PART");
            HttpResponse response = mHttpClient.execute(request);
            Log.w("SpreadsheetClient", response.getStatusLine().toString());
            is = response.getEntity().getContent();
            newSpreadsheet = new SpreadsheetFeedParser(
                    new InputStreamReader(is)).parseNewSpreadsheet();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return newSpreadsheet;
    }

    protected void shareWithTheWorld(String key) {
        InputStream is = null;
        try {
            HttpPost request = new HttpPost(new URI(
                    "http://docs.google.com/feeds/acl/private/full/spreadsheet%3A"
                            + key));
            request.setHeader("Authorization", "GoogleLogin auth="
                    + mTokens.get("writely"));
            request.setHeader("GData-Version", "2");
            String entry = null;
            try {
                StringWriter xmlEntry = new StringWriter();
                XmlSerializer serializer = XmlPullParserFactory.newInstance()
                        .newSerializer();
                serializer.setOutput(xmlEntry);
                serializer.startDocument("utf-8", null);
                serializer.setPrefix("", NS_ATOM);
                serializer.setPrefix("gAcl", NS_GACL);
                serializer.startTag(NS_ATOM, "entry").startTag(NS_ATOM,
                        "category").attribute(null, "scheme",
                        "http://schemas.google.com/g/2005#kind").attribute(
                        null, "term",
                        "http://schemas.google.com/acl/2007#accessRule")
                        .endTag(NS_ATOM, "category").startTag(NS_GACL, "role")
                        .attribute(null, "value", "reader").endTag(NS_GACL,
                                "role").startTag(NS_GACL, "scope").attribute(
                                null, "type", "default").endTag(NS_GACL,
                                "scope").endTag(NS_ATOM, "entry").endDocument();
                entry = xmlEntry.toString();
            } catch (XmlPullParserException e) {
                e.printStackTrace();
            }
            StringEntity entity = new StringEntity(entry);
            entity.setContentType("application/atom+xml");
            request.setEntity(entity);
            request.setHeader("Content-Type", "application/atom+xml");
            HttpResponse response = mHttpClient.execute(request);
            Log.w("SpreadsheetClient", response.getStatusLine().toString());
            is = response.getEntity().getContent();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    protected Person getPerson(String obj) {
        SpreadsheetParser parser = new SpreadsheetParser(obj);
        return parser.getPerson();
    }

    protected List<SpreadsheetEntry> listSpreadsheets() {
        List<SpreadsheetEntry> spreadsheetKeys = null;
        InputStream is = null;
        try {
            HttpGet request = new HttpGet(
                    new URI(
                            "http://spreadsheets.google.com/feeds/spreadsheets/private/full"));
            request.setHeader("Authorization", "GoogleLogin auth="
                    + mTokens.get("wise"));
            request.setHeader("GData-Version", "2");
            HttpResponse response = mHttpClient.execute(request);
            Log.w("SpreadsheetClient", response.getStatusLine().toString());
            if (404 == response.getStatusLine().getStatusCode())
                mResponseHandler.obtainMessage(R.id.client_http_badcode, response.getStatusLine().getReasonPhrase()).sendToTarget();
            is = response.getEntity().getContent();
            spreadsheetKeys = new SpreadsheetFeedParser(new InputStreamReader(
                    is)).parseSpreadsheetList(email);
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return spreadsheetKeys;
    }

    protected String doClientLogin(String service) {
        InputStream is = null;
        try {
            // TODO Google apps domain name case
            HttpPost request = new HttpPost(new URI(
                    "https://www.google.com/accounts/ClientLogin"));

            List<NameValuePair> params = new ArrayList<NameValuePair>();
            params
                    .add(new BasicNameValuePair("accountType",
                            "HOSTED_OR_GOOGLE"));
            params.add(new BasicNameValuePair("service", service));
            params.add(new BasicNameValuePair("source", "SFEIR-HiveGotContact-1.0"));
            params.add(new BasicNameValuePair("Email", email));
            params.add(new BasicNameValuePair("Passwd", pass));
            // params.add(new BasicNameValuePair("", ""));
            request.setEntity(new UrlEncodedFormEntity(params));
            HttpResponse response = mHttpClient.execute(request);
            Log.w("SpreadsheetClient", response.getStatusLine().toString());
            is = response.getEntity().getContent();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String curr;
            while (null != (curr = br.readLine())) {
                String[] pair = curr.split("=");
                if ("Auth".equals(pair[0]))
                    return pair[1];
                else if ("Error".equals(pair[0])) {
                    Message m = mResponseHandler
                            .obtainMessage(R.id.client_auth_failure);
                    m.obj = pair[1];
                    m.sendToTarget();
                }
            }
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            Message m = mResponseHandler.obtainMessage(R.id.request_timeout, e
                    .getMessage());
            m.sendToTarget();
        } finally {
            if (null != is)
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
        return null;
    }

    protected CellFeed getWorksheetEntry(String spreadsheetKey) {
        CellFeed listFeed = null;
        
        WorksheetFeed feed = new WorksheetFeed();
        /*HttpGDataRequest.Factory request = new HttpGDataRequest.Factory();
        GoogleAuthTokenFactory authFactory = new GoogleAuthTokenFactory("ErgoSum", "SpreadParser", new AuthTokenFactory.TokenListener() {
            @Override
            public void tokenChanged(AuthToken token) {
                mTokens.put("wise", token.toString());
            }
        });
        authFactory.setAuthSubToken(mTokens.get("wise"), null);
        SpreadsheetService service = new SpreadsheetService("ErgoSum-SpreadParser" + spreadsheetKey, request, authFactory);*/
        SpreadsheetService service = new SpreadsheetService("HiveGotContact-SpreadParser" + spreadsheetKey);
        service.setAuthSubToken(mTokens.get("wise"), null);
        InputStream is = null;
        try {
            URL spreadsheetURL = feedFactory.getWorksheetFeedUrl(spreadsheetKey, "private", "values");
            HttpGet requestworksheet = new HttpGet(
                    spreadsheetURL.toURI());
            requestworksheet.setHeader("Authorization", "GoogleLogin auth="
                    + mTokens.get("wise"));
            requestworksheet.setHeader("GData-Version", "2");
            HttpResponse response = mHttpClient.execute(requestworksheet);
            Log.w("SpreadsheetClient", response.getStatusLine().toString());
            is = response.getEntity().getContent();
            /*try {
            is = spreadsheetURL.openStream();
            } catch (FileNotFoundException fe) {
                Log.e("ErgoSum", "Spreadsheetclient getworksheetEntry", fe);
                return null;
            }*/
            feed.parseAtom(service.getExtensionProfile(), is);//service.getFeed(spreadsheetURL, WorksheetFeed.class);
            is.close();
            WorksheetEntry worksheetEntry = feed.getEntries().get(0); 

            //URL listFeedURL = worksheetEntry.getListFeedUrl();//Link listFeedURL = worksheetEntry.getLink("http://schemas.google.com/spreadsheets/2006#listfeed", "application/atom+xml");
            Link listFeedURL = worksheetEntry.getLink("http://schemas.google.com/spreadsheets/2006#cellsfeed", "application/atom+xml");
            //URL cellFeedURL = worksheetEntry.getCellFeedUrl();

            HttpGet requestlistfeed = new HttpGet(URI.create(listFeedURL.getHref()));//URI.create(listFeedURL.getHref()));
            //HttpGet requestlistfeed = new HttpGet(cellFeedURL.toURI());
            requestlistfeed.setHeader("Authorization", "GoogleLogin auth="
                    + mTokens.get("wise"));
            requestlistfeed.setHeader("GData-Version", "2");
            HttpResponse responseListfeed = mHttpClient.execute(requestlistfeed);
            Log.w("SpreadsheetClient", responseListfeed.getStatusLine().toString());
            is = responseListfeed.getEntity().getContent();

            
            /*listFeed = new ListFeed();
            listFeed.parseAtom(service.getExtensionProfile(), is);
            */
            listFeed = new CellFeed();
            listFeed.parseAtom(service.getExtensionProfile(), is);
            is.close();
            
       } catch (Exception e) {
           Log.e("ErgoSum", "Spreadsheetclient getworksheetEntry", e);
                return null;
       }
        
        return listFeed;
        
    }
 
    protected SpreadsheetFeedParser getSpreadsheetFeedParser(URI url) {
        InputStream is = null;
        try {
            HttpGet request = new HttpGet(
                    url);
            request.setHeader("Authorization", "GoogleLogin auth="
                    + mTokens.get("wise"));
            request.setHeader("GData-Version", "2");
            HttpResponse response = mHttpClient.execute(request);
            Log.w("SpreadsheetClient", response.getStatusLine().toString());
            is = response.getEntity().getContent();
            return (new SpreadsheetFeedParser(new InputStreamReader(is)));
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    
    public void terminate() {
        handler.getLooper().quit();
    }

    public synchronized void setUserCredentials(String email, String pass) {
        this.email = email;
        this.pass = pass;
    }

    public static abstract class ResponseHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case R.id.client_auth_success:
                Log.w("SpreadsheetClient", (String) msg.obj);
                break;
            case R.id.client_auth_failure:
                // TODO : handle Captcha request
                Log.e("SpreadsheetClient", (String) msg.obj);
                break;
            default:
                break;
            }
        }
    }
}
